﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using Lidgren.Network;

namespace SyncData
{
    public enum Headers // Packet Headers. Optional but useful later on.
    {
        GameWorld, // Request/Receive Information about GameWorld
        PlayerName // Send/Receive Information about Player Name alteration
    }

    class Program
    {
        static NetClient Client; // This will store our Client information.
        static NetPeerConfiguration Config; // This will store out Client configuartion.
        static List<Player> Players; // This will hold all the Players information from the server.
        static Player Player; // This will be our Player object the one client's controlling.

        static void Main(string[] args)
        {
            SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
            Console.WriteLine("This is the client.");
            Players = new List<Player>(); // Initialize the player's list.
            Config = new NetPeerConfiguration("MyGame"); // Should be the same as of the server's
            Console.WriteLine("Created client's configuration.");
            Client = new NetClient(Config);
            Console.WriteLine("Initialized client's socket");
            Client.RegisterReceivedCallback(new SendOrPostCallback(ReceiveData));
            // Register the receive callback. Better if it's before when the socket starts.
            Client.Start(); // Start the client socket
            Console.WriteLine("Started client.");
            Client.Connect("localhost", 7777); // Connect the client to the server.
            Console.WriteLine("Requesting connection to server.");

            new Thread(new ThreadStart(delegate // An example Input Manager to get respond 
            // on Console
            {
                string input;

                while ((input = Console.ReadLine()) != null)
                {
                    Console.WriteLine("New input.");
                    string[] prms = input.Split(' '); // Params for the input.

                    switch (prms[0]) // The first param is the command name.
                    {
                        case "/changename": // If the command is /changename

                            //TODO: add code later.
                            break;
                    }
                }

            })).Start();
        }

        private static void ReceiveData(object state)
        {
            NetIncomingMessage incmsg;
            NetOutgoingMessage outmsg;

            while ((incmsg = Client.ReadMessage()) != null)
            {
                Console.WriteLine("Received Data.");
                switch (incmsg.MessageType)
                {
                    case NetIncomingMessageType.StatusChanged:

                        Console.WriteLine("Status changed to " + Client.ConnectionStatus.ToString());
                        if (Client.ConnectionStatus == NetConnectionStatus.Connected)
                        {
                            Console.WriteLine("Client connected to server.");
                            outmsg = Client.CreateMessage();
                            outmsg.Write((byte)Headers.GameWorld); // Requesting Game World State.
                            Client.SendMessage(outmsg, NetDeliveryMethod.UnreliableSequenced);
                            Console.WriteLine("Send game world request to server.");
                            // Send the message to server in UDP Portocol
                        }

                        break;

                    case NetIncomingMessageType.Data:

                        Console.WriteLine("Received Data from server.");
                        Headers header = (Headers)incmsg.ReadByte();

                        switch (header)
                        {
                            case Headers.GameWorld:

                                Console.WriteLine("Received game world update.");
                                Players.Clear(); // Clears the list
                                int count = incmsg.ReadInt32(); // Get the players count
                                
                                for (int i = 0; i < count; i++) // Foreach count of player
                                {
                                    Player newPlayer = new Player(); // Create a new player for each count
                                    incmsg.ReadAllProperties(newPlayer); // Read the properties send 
                                    // from the server and put it on the new player
                                    Players.Add(newPlayer); // add that player on our player's list
                                }

                                Console.WriteLine("Updated game world state.");

                                break;
                        }

                        Client.Recycle(incmsg); // Recycled data
                        Console.WriteLine("Recycled data.");
                        break;
                }
            }
        }
    }

    public class Player
    // A basic player class. Make sure whatever you want to send back/forth is
    // a property and not a field. Like I don't want to send Connection back/forth
    // to the server so I make it on a field. 
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public int Health { get; set; }
        public string IP { get; set; } // Hold the IP of the player
        public NetConnection Connection; // Hold the Client Connection of the player

        public Player() { }
    }
}
